home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Best of MacTutor - S…e Code for Volumes 1 to 5
/
The Best of MacTutor - Source Code for Volume 1-5 (Wayzata Technology)(6031)(1990).bin
/
Source Code
/
#11 (Aug 86)
/
pascal
/
TML source
/
Sleuth.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
1986-07-16
|
19KB
|
682 lines
program KeyboardSleuth;
{ Keyboard Sleuth: analyze key mappings
Stand-alone version written in Rascal
By Joel West, August 1986, for MacTutor
** Converted to TML Pascal by David E. Smith **
Tries to figure out what keyboard is installed
Uses several approaches:
-Dump and analyze keyboard #
-Check keypad for Mac 512 vs. Mac Plus
-Look at INTL resources to find for country code
-Check for mapping of space key (US vs. Foreign)
Then allows user to type keys and shows their keycodes and ASCII values
Dumps all this to screen and to a logfile }
{ Include files and constants }
{$I MemTypes.ipas }
{$I QuickDraw.ipas }
{$I OSIntf.ipas }
{$I ToolIntf.ipas }
{$I PackIntf.ipas }
{$I HFS.ipas }
{ ---------------- GLOBAL CONSTANTS ------------ }
CONST
Key1Trans = $29E; { Low Memory Globals }
Key2Trans = $2A2;
EOL = 13; { end of line file delimiter (RETURN) }
{menu res id's }
AppleMenu = 256;
FileMenu = 257;
EditMenu = 258;
{ ---------------- ASCII values ------------ }
Space = $20; { }
{ The following are Key #10, where US,UK "/" is (key # differs in US) }
Slash = $2F; { / UK }
Minus = $2D; { - German, Spanish, Swedish }
Equals = $3D; { = French }
Ograve = $98; { ò Italian }
Eaigu = $8E; { é French Canadian }
{ The following are Key # 36, where UK "`" (accent grave) is
Used only to distinguish Spanish from German and Swedish }
Degree = $A1; { ° Spanish/Latin American }
Hash = $8A; { # German }
Apos = $27; { ' Swedish }
{ ---------------- Keycap Numbers ------------ }
USspKey = 49; { space bar in US }
UKspKey = 52; { space bar in UK and other Euro-Classics}
UKslKey = 10; { / key in UK }
UKgrKey = 36; { ` (dead) key in UK }
{ ---------------- GLOBAL VARIABLES ------------ }
VAR
{my stuff}
mywindow: WindowPtr; { our window pointer }
finished: Boolean; {program terminator}
ClockCursor:CursHandle; {handle to the waiting watch cursor}
{STDFile stuff}
logfile: INTEGER; { file status }
logname: STR255; { file name }
volNumber: INTEGER; { vRefNum }
fileNumber: INTEGER; { file number }
{Screen stuff}
DragArea: Rect; {holds the area where window can be dragged in}
GrowArea: Rect; {holds the area to which a window's size can change}
Screen: Rect; {holds the screen dimensions }
{TextEdit stuff}
DestRect: Rect;
ViewRect: Rect;
OKRect: rect; {don't invalidate this}
theText: TEHandle;
scrollflg: Boolean;
{ ---------------- BEGIN CODE ------------ }
Function KeyTrans(keyno,modifies: Integer) : Integer; EXTERNAL;
{$U keytrans }
{ Translate key number and modifiers to
their corresponding ASCII value }
{ This tries to call the country-specific keycode translator
that is loaded in location $29E. It calls the keypad translator
at Key2Trans for keycodes >= 64.
Both routines expect the keycode in register d2, and the modifiers
in the lower bits of register d1; they return an ASCII value in
register D0 }
Function CR:str255;
begin
CR:= chr(EOL)
end;
PROCEDURE Openlog;
{ open keyboard logfile to save all messages for later review }
label 1;
Var
where: Point;
Prompt: STR255;
origName: STR255;
reply: SFReply; { standard file reply record }
Info: FInfo; { Finder file info reply record }
vol: INTEGER; { vRefNum }
fileno: INTEGER; { file number }
resultCode: OSErr;
Begin
where.v := 50;
where.h := 50;
Prompt := 'Save your log file as:';
origName := 'KeyBoard Log';
DILoad; {in case disks are switched}
SFPutFile(Where, Prompt, origName, Nil, reply);
logname := reply.fName;
vol := reply.vRefNum;
IF reply.good = FALSE THEN
logfile := 0 {bad file}
ELSE
logfile:= 1; {good file}
IF logfile = 0 THEN goto 1;
resultCode:=GetFInfo (logname, vol, Info);
case resultCode of
NoErr: { file exists..delete it }
Begin
if Info.fdType <>'TEXT' then
begin
logfile:=0;
goto 1;
end;
resultCode:=RstFlock(logname,vol);
if resultCode <> NoErr then
begin
logfile:=0;
goto 1;
end;
resultCode:=FSDelete(logname,vol);
if resultCode <> NoErr then
begin
logfile:=0;
goto 1;
end;
resultCode:= Create (logname, vol, 'MACA', 'TEXT');
if resultCode <> NoErr then
begin
logfile:=0;
goto 1;
end;
end;
FNFErr: { file not found so create one }
begin
resultCode:= Create (logname, vol, 'MACA', 'TEXT');
if resultCode <> NoErr then
begin
logfile:=0;
goto 1;
end;
end;
OTHERWISE logfile:=0;
End; { case }
if logfile = 0 then goto 1;
resultCode:= FSOpen (logname, vol,fileno); { open log file }
if resultCode <> NoErr then
begin
logfile:=0;
goto 1;
end;
resultCode:= SetFPos (fileno, FSFromStart, 0);
if resultCode <> NoErr then
begin
logfile:=0;
goto 1
end;
volNumber:=vol;
fileNumber:=fileno;
1:
if logfile = 1 then
SetWTitle(mywindow, logname)
else
SetWTitle(mywindow, 'No Log File!');
End;
Procedure PutString(str: Str255);
{ Write a string to the log file and to the screen }
Var
resultCode: OSErr;
strlen: LONGINT;
scrollup: integer;
curlines: integer;
linepos: integer;
newpos: integer;
endpos: integer;
BEGIN
strlen:=length(str);
TEInsert(POINTER(ORD(@str)+1),strlen,theText);
TEIdle(theText);
HLock(handle(theText));
IF (not scrollflg) then
begin
scrollup:=theText^^.lineHeight;
curlines:=theText^^.nLines;
linepos:=curlines*scrollup;
endpos:=theText^^.ViewRect.bottom;
if (linepos>=endpos) then
scrollflg:=true;
end;
if scrollflg then TEScroll(0,-theText^^.lineHeight,theText);
HUnlock(handle(theText));
IF logfile = 1 THEN
Begin
resultCode:= FSWrite (fileNumber, strlen, POINTER(ORD(@str)+1));
if resultCode <> NoErr then logfile:=0;
End;
END;
Function IntToString(num: Integer):str255;
{integer to string}
VAR
s: Str255;
longnum: LongInt;
BEGIN
longnum:=num;
NumToString(longnum, s);
IntToString:=s;
END;
Function KbdType: Integer;
{ Fetch low memory value at $21E, a byte, indicating the keyboard number }
Type
magicHandle=^magicptr;
magicptr = ^magic;
magic = packed record
case boolean of
true: (l: longint);
false: (byte3,byte2,byte1,byte0: Byte)
end;
Var
tempHandle: Handle; {handle signed byte}
magicman: magicHandle; {handle to magic}
addr: INTEGER;
mysize: INTEGER;
BEGIN
addr:= $021E;
mysize:=SIZEOF(magicman);
tempHandle:=NewHandle(mysize);
magicman:=magicHandle(tempHandle);
magicman^:=pointer(addr);
KbdType:=magicman^^.byte3;
disposHandle(tempHandle);
END;
Procedure ShowIntlNation;
{ Show }
VAR
country: integer;
ih: intl0Hndl;
s:str255;
known: Boolean;
BEGIN
ih := intl0Hndl(IUGetIntl(0)); { get INTL 0 resource }
country := (ih^^.intl0Vers) div 16; { country is upper byte }
s:='This Mac is configured for ';
known:=true; {be optomistic}
{ There are a number of symbolic constants for these (verUS, verFrance, etc.),
but unless if you have the latest update to your development system, you
probably won't have all 26. I've hard-coded them for clarity. }
CASE country OF
0: s:=concat(s,'the US or Canada');
1: s:=concat(s,'France');
2: s:=concat(s,'U.K. or Ireland');
3: s:=concat(s,'Deutschland'); { Germany }
4: s:=concat(s,'Italia');
5: s:=concat(s,'Nederland'); { Netherlands }
6: s:=concat(s,'Belgique ou Luxembourg');
7: s:=concat(s,'Sverige'); { Sweden }
8: s:=concat(s,'Españá'); { Spain }
9: s:=concat(s,'Danmark');
10: s:=concat(s,'Portugal');
11: s:=concat(s,'Quebec'); { French Canada }
12: s:=concat(s,'Norge'); { Norway }
13: s:=concat(s,'Yisra’el');
14: s:=concat(s,'Nippon'); { Japan }
15: s:=concat(s,'Australia or New Zealand');
16: s:=concat(s,'Arabiyah');
17: s:=concat(s,'Suomi'); { Finland }
18: s:=concat(s,'Suisse'); { French Swiss }
19: s:=concat(s,'Schweiz'); { German Swiss }
20: s:=concat(s,'Ellas'); { Greece }
21: s:=concat(s,'Island'); { Iceland }
22: s:=concat(s,'Malta');
23: s:=concat(s,'Kypros'); { Cyprus }
24: s:=concat(s,'Türkiye');
25: s:=concat(s,'Jugoslavija');
OTHERWISE
Begin
known:=false;
s:=concat(s,'an unknown country, #',IntToString(country),'. ');
End;
END; {case}
if known then s:=concat(s,'. ');
s:=concat(s,CR,CR);
PutString(s);
END;
Procedure ShowModel;
{ Guess which type of Macintosh keyboard }
Var
s,ss:str255;
Kbd:INTEGER;
BEGIN
{ Use derived keyboard numbers }
Kbd:=KbdType;
ss:=IntToString(Kbd);
s:=concat('The keyboard type is ',ss);
CASE Kbd OF
11: s:=concat(s,', which is a Mac Plus keyboard.');
3: s:=concat(s,', which is the Classic Mac keyboard.');
OTHERWISE s:=concat(s,', which is unknown.');
END; {case}
s:=concat(s,CR);
PutString(s);
END;
Procedure GuessKeyNation;
{ Guess which country keyboard mappings are set for }
Var
s: str255;
BEGIN {proc}
{ Try mapping of certain keys to figure US vs. non-US keyboard }
IF (KeyTrans(USspKey,0) = Space) THEN
begin
s:='This is US, Canadian or down under.';
end {IF..THEN}
ELSE
BEGIN
IF (KeyTrans(UKspKey,0) = Space) THEN
BEGIN
{ Use UK "/" key to guess at nationality }
CASE KeyTrans(UKslKey,0) OF
Slash: { / UK }
s:=concat(s,'I am British or Dutch.');
Ograve: { ò Italian }
s:=concat(s,'Sono Italiano.');
Equals: { = French }
s:=concat(s,'Je suis français, suisse ou belge.');
Eaigu: { é French Canadian }
s:=concat(s,'Je suis canadien.');
Minus: { - German, Spanish, Swedish }
{ Use UK accent grave (dead `) to tell
German, Spanish, and Swedish }
CASE KeyTrans(UKgrKey,0) OF
Hash: { # German }
s:=concat(s,'Ich bin ein Deutscher.');
Degree: { ç Spanish }
s:=concat(s,'Habla Español.');
Apos: { ' Swedish }
s:=concat(s,'This is Swedish.');
OTHERWISE { I have no country! }
s:=concat(s,'¡No tengo un país!');
END; {case UKgrKey}
OTHERWISE
begin
s:=concat(s,'I am a Mac without a country!');
end; {otherwise}
END; {CASE}
END {IF...THEN}
ELSE
begin
s:=concat(s,'Neither US nor European, what is it?');
end; {else}
END; {IF..THEN..ELSE}
s:=concat(s,CR,CR,'Type keys, or click mouse to quit.',CR);
PutString(s);
END; {proc}
Procedure DoMyStuff;
Var
s: str255;
BEGIN
OpenLog; { log file }
ShowIntlNation; { Find country code }
ShowModel; { Examine keyboard type }
GuessKeyNation; { Look at key mappings }
showWindow(mywindow);
END;
{ ----- Following code is standard Mac Shell stolen from TML Examples ---}
PROCEDURE DoMenu(select:longint);
Var Menu_No: integer; {menu selected}
Item_No: integer; {item selected}
NameHolder: Str255; {DA or Font name holder }
DNA: integer; {OpenDA result}
Begin
If select <> 0 then
begin
Menu_No := HiWord(select); {get the Hi word of...}
Item_no := LoWord(select); {get the Lo word of...}
Case Menu_No of
AppleMenu:
Begin
GetItem(GetMHandle(AppleMenu), Item_No, NameHolder);
DNA := OpenDeskAcc(NameHolder);
End; {applemenu}
FileMenu: Finished:=true; {quit}
EditMenu:
Begin
If Not SystemEdit(Item_no - 1)
then
Case Item_No of
1: begin end; {undo}
{ 2: line divider}
3: TECut(theText); {cut}
4: TECopy(theText ); {copy}
5: TEPaste(theText ); {paste}
6: TEDelete(theText ); {clear}
End; {case}
End; {editmenu}
end; {case menu_no}
HiliteMenu(0); {unhilite after processing menu}
end; {If select <> 0}
End; {of DoMenu procedure}
PROCEDURE doMouseDowns(Event:EventRecord);
Var Location :integer;
WindowPointedTo :WindowPtr;
MouseLoc :Point;
WindoLoc :integer;
Begin
MouseLoc := Event.Where;
WindoLoc := FindWindow(MouseLoc, WindowPointedTo);
Case WindoLoc of
inMenuBar: DoMenu(MenuSelect(MouseLoc));
inSysWindow: SystemClick(Event,WindowPointedTo);
inContent:
if WindowPointedto <> FrontWindow then
SelectWindow(WindowPointedTo);
inGrow: Begin End; {no grow}
inDrag: DragWindow(WindowPointedTo,MouseLoc,DragArea);
inGoAway:
Begin
If TrackGoAway(WindowPointedTo,MouseLoc) then
Begin
DisposeWindow(WindowPointedTo);
finished:=true;
End;
End; {inGoAway}
End{ of case};
End;
PROCEDURE doKeyDowns(Event:EventRecord);
Type
magicHandle=^magicptr;
magicptr = ^magic;
magic = packed record
case boolean of
true: (l: longint);
false: (byte3,byte2,byte1:Byte;chr0: Char)
end;
Var CharCode: char;
keycode: Byte;
mods: INTEGER;
s: str255;
keyc: INTEGER;
asc: INTEGER;
tempHandle: Handle; {handle to signed byte}
magicman: magicHandle; {handle to magic}
mysize: INTEGER;
Begin
mysize:=SIZEOF(magicman);
tempHandle :=NewHandle(mysize);
magicman :=magicHandle(tempHandle);
magicman^^.l:=Event.message;
CharCode :=magicman^^.chr0;
keycode :=magicman^^.byte1;
keyc := keycode;
mods := Event.modifiers;
s:=concat('Key #',IntToString(keyc));
IF BitAnd(mods,optionKey) = optionKey THEN
s:=concat(s,' with Option');
IF BitAnd(mods,shiftKey) = shiftKey THEN
s:=concat(s,', shifted');
IF BitAnd(mods,alphaLock) = alphaLock THEN
s:=concat(s,', Caps Locked');
asc := KeyTrans(keyc,mods); { translate to ASCII }
{ Don't want to print control characters }
IF asc >= 32 THEN
BEGIN
s:=concat(s,' is ',chr(asc),' (ascii ',IntToString(asc),').');
END;
s:=concat(s,CR);
PutString(s)
END;
PROCEDURE doActivates(Event: EventRecord);
Var TargetWindow:WindowPtr;
Begin
TargetWindow := pointer(ord4(Event.message));
If Odd(Event.modifiers) then
Begin {activate}
SetPort(TargetWindow);
End
else {deactivate}
Begin End;
End;
PROCEDURE doUpdates(Event:EventRecord);
Var
UpDateWindow,TempPort: WindowPtr;
Begin
UpDateWindow := pointer(ord4(Event.message)); {typecasting}
if UpDateWindow = mywindow then
Begin
GetPort(TempPort); {Save the current port}
SetPort(mywindow); {set the port to one in Evt.msg}
BeginUpDate(mywindow);
EraseRect(mywindow^.visRgn^^.rgnBBox);
TEUpdate(mywindow^.visRgn^^.rgnBBox,theText); {update window contents}
EndUpDate(mywindow);
SetPort(TempPort); {restore to the previous port}
End;
End;
PROCEDURE EndProgram;
Var
resultcode: OSErr;
Begin
IF logfile =1 THEN
begin
resultCode:= FSClose(fileNumber);
end;
ExitToShell;
End;
PROCEDURE MainEventLoop;
Var Event:EventRecord;
DoIt: Boolean;
Begin
InitCursor;
Repeat
SystemTask; {support DAs}
DoIt := GetNextEvent(EveryEvent,Event);
If DoIt{is true} then {we'll DoIt}
Case Event.what of
mouseDown : doMouseDowns(Event); {1}
mouseUp : begin end; {2}
KeyDown : doKeyDowns (Event); {3}
keyUp : begin end; {4}
autoKey : begin end; {5}
updateEvt : doUpdates (Event); {6}
diskEvt : begin end; {7}
activateEvt : doActivates (Event); {8}
{abort evt now reserved for future} {9}
networkEvt : begin end; {A}
driverEvt : begin end; {B}
app1Evt : begin end; {C}
app2Evt : begin end; {D}
app3Evt : begin end; {E}
app4Evt : begin end; {F}
End;{of Case}
Until Finished; {end program}
EndProgram;
End;
PROCEDURE InitThings;
Begin
InitGraf(@thePort);
ClockCursor := GetCursor(watchCursor);
HLock(Handle(ClockCursor));
SetCursor(ClockCursor^^);
InitFonts;
InitWindows;
InitMenus;
TEInit;
InitDialogs(Nil);
FlushEvents(everyEvent,0);
scrollflg:=false; {too early to scroll!}
finished:=false; {clear program terminator}
End;
PROCEDURE SetupLimits;
Begin
Screen := ScreenBits.Bounds; {set screen 512 by 342 pixels}
SetRect(DragArea,Screen.left+4,Screen.top+24,Screen.right-4,Screen.bottom-4);
SetRect(GrowArea,Screen.left,Screen.top+24,Screen.right,Screen.bottom);
End;
Procedure SetupWindows;
Const
sbarwidth=16;
Var myrect: Rect;
windtype: integer;
Visible: boolean;
GoAway: boolean;
RefVal: LongInt;
Begin
SetRect(myrect,10,40,500,330); {set the size of the window -global coordinates}
windtype := 4; {set window type - nogrowdocproc }
Visible := false; {set the window to invisible }
GoAway := true; {give the window a GoAway box }
mywindow:= NewWindow(Nil, { Window Mgr will allocate space in Heap}
myrect, { rectangle with windows size}
'Keyboard Sleuth',{ the title of the window }
Visible, { set the window to invisible }
windtype, { Window definition ID}
POINTER(-1), { behind ptr: window is set to front}
GoAway, { draw a goaway region in title area }
RefVal); { 32-bit value that can be used by App}
SetPort(mywindow);
TextFont(Geneva);
{ Set Up Text Edit Record for this Window }
with myWindow^.portRect do
SetRect(ViewRect,4,4,right-(sbarwidth-1),bottom-(sbarwidth-1));
DestRect:=ViewRect;
theText:= TENew(DestRect,ViewRect)
{NOTE: NewWindow have initiated an ActivatEvt and an }
{ UpDateEvt event. They are being queued up by the event manager.}
{ Also, the window record is Non-relocatable, so put it on the stack}
{ or create it so that it is low in the heap space, reduce fragmentation}
End;
PROCEDURE SetupMenus;
Var myMenu :MenuHandle;
NameHolder :STR255;
Begin
myMenu := GetMenu(AppleMenu);{from resource file}
AddResMenu(myMenu,'DRVR'); {adds DAs}
InsertMenu(myMenu,0); {put list in menu}
myMenu := GetMenu(FileMenu); {Quiting...}
InsertMenu(myMenu,0);
myMenu := GetMenu(EditMenu); {DA support...}
InsertMenu(myMenu,0);
DrawMenuBar; {show the menu bar}
End;
{ ---------------- MAIN PROGRAM ------------ }
BEGIN
InitThings;
SetupLimits;
SetupWindows; {do first so its low in heap}
SetupMenus;
DoMyStuff;
MainEventLoop;
END.